unit hademapping;

{$I ..\hddefines.inc}
{$mode objfpc}{$H+}

interface

uses
  Classes,
  SysUtils,
  hademapbase,
  hadeobject;

type
  THadeMapProperties = class;
  { THadeColumn }

  THadeColumn = class
  private
    FColInfo: TSetPtkInfo;
    FColName: string;
    FFieldType: THadeFieldType;
    FLength: integer;
    FProp: THadeMapProperties;
  public
    property ColumnName: string read FColName write FColName;
    property Length: integer read FLength write FLength default 0;
    property ColInfo: TSetPtkInfo read FColInfo write FColInfo;
    property FieldType: THadeFieldType read FFieldType write FFieldType;

    property MapProperty: THadeMapProperties read FProp write FProp;

    constructor Create(const AColName: string; const AFieldType: THadeFieldType;
      const AColInfo: TSetPtkInfo = []; const ALength: integer = 0);
    destructor Destroy; override;
  end;

  { THadeTable }

  THadeTable = class(THadeMapBase)
  private
    FTableName: string;

    function getTableName: string;
    procedure setTableName(AValue: string);
  public
    property TableName: string read getTableName write setTableName;

    function add(const AName: shortstring; const AObject: TObject): integer; override;
    function getPK: THadeColumn;

    constructor Create(const ATableName: string);
  end;

  { THadeTableList }

  THadeTableList = class(THadeMapBase)
  protected
    function getItem(index: integer): THadeTable; reintroduce;
  public
    property Items[index: integer]: THadeTable read getItem;
    function find(const s: shortstring): THadeTable; reintroduce;
    function TableByName(const ATableName: string): THadeTable;
  end;

  { THadeMapProperties }

  THadeMapProperties = class
  private
    FColumn: THadeColumn;
    FPropName: string;
  public
    property Column: THadeColumn read FColumn write FColumn;
    property PropName: string read FPropName;

    constructor Create(const APropName: string; const AColumn: THadeColumn);
  end;

  { THadeRegObject }

  THadeRegObject = class
  private
    FBOName: string;
  public
    property ClassBOName: string read FBOName write FBOName;//THadeObject class Name
  end;

  ThadeMapClass = class;

  { THadeRegObjectList }
  { list of THadeRegObject }
  THadeRegObjectList = class(THadeMapBase)
  protected
    function getItem(index: integer): THadeRegObject; reintroduce;
  public
    property Items[index: integer]: THadeRegObject read getItem;
    function find(const s: shortstring): THadeRegObject; reintroduce;

    //function ClassByBOName(const s: shortstring): THadeMapClass;
  end;

  { THadeMapClass }

  THadeMapClass = class(THadeMapBase)
  private
    fRefClass: THadeMapSimpleValue;
    FSelfRef: THadeObjectClass;
    FTable: THadeTable;

    function getTable: THadeTable;
    procedure setTable(AValue: THadeTable);
  protected
    function getItem(index: integer): THadeMapProperties; reintroduce;
  public
    property Items[index: integer]: THadeMapProperties read getItem;
    function find(const s: shortstring): THadeMapProperties; reintroduce;

    property Table: THadeTable read getTable write setTable;
    property SelfRef: THadeObjectClass read FSelfRef write FSelfRef;

    function PropByName(const APropName: string): THadeMapProperties;
    function PropNameByCol(const AColName: string): string;
    function ColNameByProp(const APropName: string): string;
    function getWriteableProps: TStringList;
    function getWriteableColumn: TStringList;
    function getReadableColumn: TStringList;
    function getReadableProps: TStringList;
  end;

  { THadeMapClassList }

  THadeMapClassList = class(THadeMapBase)
  private

  protected
    function getItem(index: integer): THadeMapClass; reintroduce;
  public
    property Items[index: integer]: THadeMapClass read getItem;
    function find(const s: shortstring): THadeMapClass; reintroduce;

    function ClassByName(const AClassName: string): THadeMapClass;
  end;

  { THadeMappingManager }

  THadeMappingManager = class
  private
    FMapClassList: THadeMapClassList;
    FRegObjectList: THadeRegObjectList;
    FTableList: THadeTableList;
  public
    constructor Create;
    destructor Destroy; override;

    property MapClassList: THadeMapClassList read FMapClassList;
    property TableList: THadeTableList read FTableList;
    property RegObjectList: THadeRegObjectList read FRegObjectList;

    function ClassByName(const AName: string): THadeMapClass;
    function TableByName(const AName: string): THadeTable;
    function RegObjecByName(const AName: string): THadeRegObject;

    procedure LoadMappingFile(const AFileName: string);

    procedure RegisterMapping(const AObjClass: THadeObjectClass;
      const ATable, APropName, AColName: string; const AFieldType: THadeFieldType;
      const ALength: integer = 0; const ASetPtkInfo: TSetPtkInfo = []);
    procedure RegisterObjectList(const AClassObjectList: THadeObjectListClass;
      const AObjectClass: THadeObjectClass);
  end;

  { THadeMappingConnection }

  THadeMappingConnection = class(THadeMapBase)
  protected

  public
    function Find(const s: shortstring): THadeMappingManager; reintroduce;
    procedure RegisterMapping(const AConnectionName: string;
      const AObjClass: THadeObjectClass; const ATable, APropName, AColName: string;
      const AFieldType: THadeFieldType; const ALength: integer = 0;
      const ASetPtkInfo: TSetPtkInfo = []);

    procedure RegisterObjectList(const AConnectionName: string;
      const AClassObjectList: THadeObjectListClass;
      const AObjectClass: THadeObjectClass);
  end;

function GHadeMappingManager: THadeMappingConnection;

implementation

uses
  hadeexception,
  hadeconst,
  hadeconnectionmanager,
  dbugintf;

var
  FHadeMappingConnection: THadeMappingConnection = nil;

function GHadeMappingManager: THadeMappingConnection;
begin
  { TODO 1 -oFajar -cOPF : make thread safe }
  if not Assigned(FHadeMappingConnection) then
    FHadeMappingConnection := THadeMappingConnection.Create;
  Result := FHadeMappingConnection;
end;

{ THadeMappingConnection }

function THadeMappingConnection.Find(const s: shortstring): THadeMappingManager;
var
  tmp: string;
begin
  if trim(s) = '' then
  begin
    tmp := GHadeConnectionManager.getDefaultConnectionName;
    Result := THadeMappingManager(inherited find(tmp));
  end
  else
    Result := THadeMappingManager(inherited find(s));
end;

procedure THadeMappingConnection.RegisterMapping(const AConnectionName: string;
  const AObjClass: THadeObjectClass; const ATable, APropName, AColName: string;
  const AFieldType: THadeFieldType; const ALength: integer;
  const ASetPtkInfo: TSetPtkInfo);
var
  MappingMgr: THadeMappingManager;
begin
  MappingMgr := find(AConnectionName);
  if not Assigned(MappingMgr) then
  begin
    MappingMgr := THadeMappingManager.Create;
    self.add(AConnectionName, MappingMgr);
  end;

  MappingMgr.RegisterMapping(AObjClass,
    ATable,
    APropName,
    AColName,
    AFieldType,
    ALength,
    ASetPtkInfo);
end;

procedure THadeMappingConnection.RegisterObjectList(const AConnectionName: string;
  const AClassObjectList: THadeObjectListClass; const AObjectClass: THadeObjectClass);
var
  mappingMgr: THadeMappingManager;
begin
  mappingMgr := find(AConnectionName);
  if not Assigned(mappingMgr) then
    raise EHadeMap.CreateFmt('Connection with %s name not found.', [AConnectionName]);
  MappingMgr.RegisterObjectList(AClassObjectList, AObjectClass);
end;

{ THadeRegObjectList }

function THadeRegObjectList.getItem(index: integer): THadeRegObject;
begin
  Result := THadeRegObject(inherited getItem(index));
end;

function THadeRegObjectList.find(const s: shortstring): THadeRegObject;
begin
  Result := THadeRegObject(inherited find(s));
end;

//function THadeRegObjectList.ClassByBOName(const s: shortstring): THadeMapClass;
//begin
//  Result := THadeRegObject(inherited find(s)).ClassBOName;
//end;

{ THadeTableList }

function THadeTableList.getItem(index: integer): THadeTable;
begin
  Result := THadeTable(inherited getItem(index));
end;

function THadeTableList.find(const s: shortstring): THadeTable;
begin
  Result := THadeTable(inherited find(s));
end;

function THadeTableList.TableByName(const ATableName: string): THadeTable;
begin
  Result := THadeTable(find(ATableName));
end;

{ THadeMapClassList }

function THadeMapClassList.getItem(index: integer): THadeMapClass;
begin
  Result := THadeMapClass(inherited getItem(index));
end;

function THadeMapClassList.find(const s: shortstring): THadeMapClass;
begin
  Result := THadeMapClass(inherited find(s));
end;

function THadeMapClassList.ClassByName(const AClassName: string): THadeMapClass;
begin
  Result := THadeMapClass(find(AClassName));
end;

{ THadeMappingManager }

constructor THadeMappingManager.Create;
begin
  FMapClassList := THadeMapClassList.Create(True);
  FTableList := THadeTableList.Create(True);
  FRegObjectList := THadeRegObjectList.Create(True);
end;

destructor THadeMappingManager.Destroy;
begin
  FreeAndNil(FMapClassList);
  FreeAndNil(FTableList);
  FreeAndNil(FRegObjectList);
  inherited Destroy;
end;

function THadeMappingManager.ClassByName(const AName: string): THadeMapClass;
begin
  Result := Self.FMapClassList.Find(AName);
end;

function THadeMappingManager.TableByName(const AName: string): THadeTable;
begin
  Result := FTableList.Find(AName);
end;

function THadeMappingManager.RegObjecByName(const AName: string): THadeRegObject;
begin
  Result := FRegObjectList.find(AName);
end;

procedure THadeMappingManager.LoadMappingFile(const AFileName: string);
begin
  { TODO 2 -oFajar -cOPF : implement LoadMappingFile() }
end;

procedure THadeMappingManager.RegisterMapping(const AObjClass: THadeObjectClass;
  const ATable, APropName, AColName: string; const AFieldType: THadeFieldType;
  const ALength: integer; const ASetPtkInfo: TSetPtkInfo);
var
  Col: THadeColumn;
  Prop: THadeMapProperties;
  Table: THadeTable;
  MapClass: THadeMapClass;
begin
  Col := THadeColumn.Create(AColName, AFieldType, ASetPtkInfo, Alength);
  Prop := THadeMapProperties.Create(APropName, Col);
  Col.MapProperty := Prop;

  MapClass := THadeMapClass(FMapClassList.Find(AObjClass.ClassName));
  if not Assigned(MapClass) then
  begin
    MapClass := THadeMapClass.Create;
    Table := THadeTable.Create(ATable);
    FTableList.add(ATable, Table);
    MapClass.Table := Table;
    MapClass.SelfRef := AObjClass;
    FMapClassList.add(AObjClass.ClassName, MapClass);
  end;

  MapClass.Table.add(AColName, Col);
  MapClass.add(APropName, Prop);
end;

procedure THadeMappingManager.RegisterObjectList(
  const AClassObjectList: THadeObjectListClass; const AObjectClass: THadeObjectClass);
var
  fregObj: THadeRegObject;
begin
  fregObj := THadeRegObject.Create;
  fregObj.ClassBOName := AObjectClass.ClassName;

  FRegObjectList.add(AClassObjectList.ClassName, fregObj);
end;

{ THadeMapClass }

function THadeMapClass.getTable: THadeTable;
begin
  Result := Self.FTable;
end;

procedure THadeMapClass.setTable(AValue: THadeTable);
begin
  self.FTable := AValue;
end;

function THadeMapClass.getItem(index: integer): THadeMapProperties;
begin
  Result := THadeMapProperties(inherited getItem(index));
end;

function THadeMapClass.find(const s: shortstring): THadeMapProperties;
begin
  Result := THadeMapProperties(inherited find(s));
end;

function THadeMapClass.PropByName(const APropName: string): THadeMapProperties;
begin
  Result := THadeMapProperties(find(APropName));
end;

function THadeMapClass.PropNameByCol(const AColName: string): string;
begin
  Result := THadeColumn(Table.Find(AColName)).MapProperty.PropName;
end;

function THadeMapClass.ColNameByProp(const APropName: string): string;
begin
  Result := PropByName(APropName).Column.ColumnName;
end;

function THadeMapClass.getWriteableProps: TStringList;
var
  iloop: integer;
begin
  Result := TStringList.Create;
  for iloop := 0 to pred(Count) do
  begin
    if not (ptkReadOnly in Items[iloop].Column.FColInfo) then
      Result.Add(self.NameOfIndex(iloop));
  end;
end;

function THadeMapClass.getWriteableColumn: TStringList;
var
  iloop: integer;
begin
  Result := TStringList.Create;
  for iloop := 0 to pred(Count) do
  begin
    if not (ptkReadOnly in Items[iloop].Column.FColInfo) then
      Result.Add(items[iloop].Column.ColumnName);
  end;
end;

function THadeMapClass.getReadableColumn: TStringList;
var
  iloop: integer;
begin
  Result := TStringList.Create;
  for iloop := 0 to pred(Count) do
  begin
    if not (ptkWriteOnly in Items[iloop].Column.FColInfo) then
      Result.Add(items[iloop].Column.ColumnName);
  end;
end;

function THadeMapClass.getReadableProps: TStringList;
var
  iloop: integer;
begin
  Result := TStringList.Create;

  for iloop := 0 to pred(Count) do
  begin
    if not (ptkWriteOnly in Items[iloop].Column.FColInfo) then
      Result.Add(NameOfIndex(iloop));
  end;
end;

{ THadeTable }

function THadeTable.getTableName: string;
begin
  Result := self.FTableName;
end;

procedure THadeTable.setTableName(AValue: string);
begin
  if Self.FTableName = AValue then
    exit;
  Self.FTableName := AValue;
end;

function THadeTable.add(const AName: shortstring; const AObject: TObject): integer;

  function isPKExists: boolean;
  begin
    Result := False;
    if Assigned(getPK) then
      Result := True;
  end;

begin
  if ptkPK in THadeColumn(AObject).ColInfo then
  begin
    if isPKExists then
      raise EHadePkExists.createFmt(CHAdePkExists, [fTableName]);
  end;
  Result := inherited add(AName, AObject);
end;

function THadeTable.getPK: THadeColumn;
var
  iloop: integer;
  tmp: THadeColumn;
begin
  Result := nil;
  for iloop := 0 to pred(self.Count) do
  begin
    tmp := THadeColumn(self.Items[iloop]);
    if ptkPK in tmp.ColInfo then
    begin
      Result := tmp;
      break;
    end;
  end;
end;

constructor THadeTable.Create(const ATableName: string);
begin
  inherited Create(True);
  Self.TableName := ATableName;
end;

{ THadeColumn }

constructor THadeColumn.Create(const AColName: string;
  const AFieldType: THadeFieldType; const AColInfo: TSetPtkInfo;
  const ALength: integer);
begin
  Self.FColName := AColName;
  Self.FieldType := AFieldType;
  self.ColInfo := AColInfo;
  self.Length := ALength;
end;

destructor THadeColumn.Destroy;
begin
  inherited Destroy;
end;

{ THadeMapProperties }

constructor THadeMapProperties.Create(const APropName: string;
  const AColumn: THadeColumn);
begin
  self.Column := AColumn;
  FPropName := APropName;
end;

initialization
  GHadeMappingManager();
finalization
  if Assigned(FHadeMappingConnection) then
    FreeAndNil(FHadeMappingConnection);
end.

